using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;


namespace Rogue
{
    public class LineHelper : IDisposable
    {
        #region LINE STRUCT DECLARATION
        // Struct for the line. Instances of this struct will be added to the line list.
        struct Line
        {
            public Point m_StartPoint, m_EndPoint;
            public Color m_Color;

            // Constructor
            public Line(Point a_pStartPoint, Point a_pEndPoint, Color a_cColor)
            {
                m_StartPoint = a_pStartPoint;
                m_EndPoint = a_pEndPoint;
                m_Color = a_cColor;
            }

            // Override equality check for line
            public static bool operator ==(Line a, Line b)
            {
                return (a.m_StartPoint == b.m_StartPoint) &&
                    (a.m_EndPoint == b.m_EndPoint) &&
                    (a.m_Color == b.m_Color);
            }

            // Override inequality check since we've only done equality
            public static bool operator !=(Line a, Line b)
            {
                return a.m_StartPoint != b.m_StartPoint ||
                    a.m_EndPoint != b.m_EndPoint ||
                    a.m_Color != b.m_Color;
            }

            // Override equals operator so compiler doesn't bitch at us
            public override bool Equals(object a)
            {
                if (a.GetType() == typeof(Line))
                    return (Line)a == this;
                else
                    return false;
            }

            // Override GetHashCode so compiler doesn't bitch at us
            public override int GetHashCode()
            {
                return 0;
            }
        }
        #endregion

        // The main list of 2D lines
        private List<Line> m_LineList = new List<Line>();

        private int m_nNumLines;                    // Number of lines we're waiting to draw
        private const int m_nMaxLines = 1024;        // Max number of lines in the buffer
        private int m_nPrimitiveCount = 0;          // Current number of primitives
        private bool m_bBuildVertexBuffer = false;  // Tells the line manager that the vertex buffer needs to be rebuilt
        private BasicEffect LineEffect;
        
        // Vertex buffer
        VertexPositionColor[] m_LineVertices = new VertexPositionColor[m_nMaxLines * 2];

        // Vertex declaration for the lines
        VertexDeclaration m_VertexDecl = null;
       
        // ============================================================================
        // FUNC: Constructor
        // INFO: Creates the vertex declaration and other init stuff
        // ============================================================================
        public LineHelper(GraphicsDevice a_Device )
        {
            m_VertexDecl = new VertexDeclaration(a_Device, VertexPositionColor.VertexElements);
            LineEffect = new BasicEffect(a_Device, null);
   
            LineEffect.VertexColorEnabled = true;
            LineEffect.World = Matrix.Identity;
            LineEffect.Projection = Matrix.CreateOrthographicOffCenter(0, 1280, 720, 0, 0, 1);
        }
        
        
        // ============================================================================
        // FUNC: Render
        // INFO: Puts the lines on the screen
        // ============================================================================
        public virtual void Draw(GraphicsDevice a_Device)
        {
            // Update our vertex buffer if need be
            if (m_bBuildVertexBuffer || m_nPrimitiveCount != m_nNumLines)
                BuildVertexBuffer();
                    a_Device.VertexDeclaration = m_VertexDecl;
     
            a_Device.RenderState.CullMode = CullMode.None;

            // Render any lines we got
            if (m_nPrimitiveCount > 0)
            {
                // Console.WriteLine("Drawing the lines!");
                LineEffect.Begin();
                
                    LineEffect.CurrentTechnique.Passes[0].Begin();
            
                    a_Device.DrawUserPrimitives<VertexPositionColor>(
                        PrimitiveType.LineList, m_LineVertices, 0, m_nPrimitiveCount);

                    // Set number of lines to 0. We're done drawing on this pass
                    LineEffect.CurrentTechnique.Passes[0].End();
   
                LineEffect.End();
                m_nNumLines = 0;
            }
        }


        // ============================================================================
        // FUNC: UpdateVertexBuffer
        // INFO: Self Explanatory
        // ============================================================================
        public void BuildVertexBuffer()
        {
            // Don't do anything without any lines to draw
            if (m_nNumLines == 0 || m_LineList.Count < m_nNumLines)
            {
                m_nPrimitiveCount = 0;
                return;
            }

            for (int nCurrentLine = 0; nCurrentLine < m_nNumLines; nCurrentLine++)
            {
                // Set up our defaults
                Line TempLine = (Line)m_LineList[nCurrentLine];
                Vector3 vTempVertex = new Vector3(0.0f, 0.0f, 0.0f);
                
                // Set up the first vertex
                vTempVertex.X = TempLine.m_StartPoint.X;
                vTempVertex.Y = TempLine.m_StartPoint.Y;
                vTempVertex.Z = 0.0f;

                // Add the vert to the buffer
                m_LineVertices[nCurrentLine * 2 + 0] = new VertexPositionColor(vTempVertex, TempLine.m_Color);

                // Set up the second vertex
                vTempVertex.X = TempLine.m_EndPoint.X;
                vTempVertex.Y = TempLine.m_EndPoint.Y;
                vTempVertex.Z = 0.0f;

                // Add the vert to the buffer
                m_LineVertices[nCurrentLine * 2 + 1] = new VertexPositionColor(vTempVertex, TempLine.m_Color);
            }

            // Set the count to match the number of lines
            m_nPrimitiveCount = m_nNumLines;

            // Reset the flag
            m_bBuildVertexBuffer = false;
        }


        // ============================================================================
        // FUNC: AddLine
        // INFO: Adds another line into m_LineList
        // ============================================================================
        public void AddLine(Point a_StartPoint, Point a_EndPoint, Color a_Color)
        {
            // Check to see if we've blown our budget
            if (m_nNumLines >= m_nMaxLines)
            {
                //System.Console.WriteLine("Too many lines requested in LineManager2D!");
                return;
            }
            
            // Build the line
            Line TempLine = new Line(a_StartPoint, a_EndPoint, a_Color);


            // Check to see if this line exists already (slow?)
            if (m_LineList.Count > m_nNumLines)
            {
                if ((Line)m_LineList[m_nNumLines] != TempLine)
                {
                    m_LineList[m_nNumLines] = TempLine; // Overwrite the old one
                    m_bBuildVertexBuffer = true;        // Build the vertex buffer again
                }
            }
            else
            {
                m_LineList.Add(TempLine);           // Just add a new line
                m_bBuildVertexBuffer = true;        // Build the vertex buffer again
            }

            // We now have one more line in the pool
            m_nNumLines++;
        }


        // ============================================================================
        // FUNC: AddLineWithShadow
        // INFO: Adds a black line offset downward from the original line, then 
        // adds the real line
        // ============================================================================
        public void AddLineWithShadow(Point a_StartPoint, Point a_EndPoint, Color a_Color)
        {
            AddLine(new Point(a_StartPoint.X, a_StartPoint.Y + 1), new Point(a_EndPoint.X, a_EndPoint.Y + 1), Color.Black);
            AddLine(a_StartPoint, a_EndPoint, a_Color);
        }


        // ============================================================================
        // FUNC: Dispose
        // INFO: Dispose method (so we can use IDisposable and clear our vertex 
        // buffer manually)
        // ============================================================================
        public void Dispose()
        {
            if (m_VertexDecl != null)
                m_VertexDecl.Dispose();
            m_VertexDecl = null;
        }
    }


}
